﻿#include "FITKGraphObjectSketchGrid.h"

// C++
#include <cmath>

// VTK
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkCamera.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPoints.h>
#include <vtkProperty.h>
#include <vtkVector.h>
#include <vtkMath.h>
#include <vtkInteractorObserver.h>
#include <vtkTextActor.h>
#include <vtkTextProperty.h>

// Data
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeoCommandList.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKAbsGeoSketch2D.h"

// Graph
#include "FITKGraphCommons.h"
#include "FITKGraphObjectSketch2D.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

namespace Exchange
{
    FITKGraphObjectSketchGrid::FITKGraphObjectSketchGrid(AbaqusData::FITKDataCase* caseData, Interface::FITKGeoCommandList* cmdList)
        : FITKGraphObject3D(caseData, cmdList)
    {
        if (!cmdList)
        {
            return;
        }

        // Initialize.
        init();

        // Try to generate the dataset.
        generateDataSet();

        // Generate the grid actors.
        generateGraph();

        // Set the layer need to be rendered.
        setRenderLayer(1);

        // Save if the bounds are fixed or dynamic.
        m_hasFixedBounds = true;
    }

    FITKGraphObjectSketchGrid::~FITKGraphObjectSketchGrid()
    {
        // Delete the grid lines data.
        deleteVtkObj(m_gridForMain);
        deleteVtkObj(m_gridForSub);
        deleteVtkObj(m_gridForAxis);
        deleteVtkObj(m_gridForCross);
        deleteVtkObj(m_pointsForMain);
        deleteVtkObj(m_pointsForSub);
        deleteVtkObj(m_pointsForAxis);
        deleteVtkObj(m_pointsForCross);

        m_currentSketchGraphObj = nullptr;
    }

    void FITKGraphObjectSketchGrid::init()
    {
        // Create the grid lines.
        m_gridForMain = vtkUnstructuredGrid::New();
        m_pointsForMain = vtkPoints::New();
        m_gridForMain->SetPoints(m_pointsForMain);

        m_gridForSub = vtkUnstructuredGrid::New();  
        m_pointsForSub = vtkPoints::New();
        m_gridForSub->SetPoints(m_pointsForSub);

        m_gridForAxis = vtkUnstructuredGrid::New();
        m_pointsForAxis = vtkPoints::New();
        m_gridForAxis->SetPoints(m_pointsForAxis);

        // Initialize the grid points.
        int nMainPoints = pow(m_numGrid + 1, 2);
        for (int i = 0; i < nMainPoints; i++)
        {
            m_pointsForMain->InsertNextPoint(0., 0., 0.);
        }

        int nSubPoints = pow(m_numGrid * m_numSubGrid + 1, 2);
        for (int i = 0; i < nSubPoints; i++)
        {
            m_pointsForSub->InsertNextPoint(0., 0., 0.);
        }

        for (int i = 0; i < 4; i++)
        {
            m_pointsForAxis->InsertNextPoint(0., 0., 0.);
        }

        // Initialize the grid cells.
        initGridCells(m_gridForMain, m_numGrid);
        initGridCells(m_gridForSub, m_numGrid * m_numSubGrid);

        vtkSmartPointer<vtkIdList> cellHoriAxis = vtkSmartPointer<vtkIdList>::New();
        cellHoriAxis->InsertNextId(0);
        cellHoriAxis->InsertNextId(1);

        vtkSmartPointer<vtkIdList> cellVertAxis = vtkSmartPointer<vtkIdList>::New();
        cellVertAxis->InsertNextId(2);
        cellVertAxis->InsertNextId(3);

        m_gridForAxis->InsertNextCell(VTKCellType::VTK_LINE, cellHoriAxis);
        m_gridForAxis->InsertNextCell(VTKCellType::VTK_LINE, cellVertAxis);

        // Create the cross symbol.
        m_gridForCross = vtkUnstructuredGrid::New();
        m_pointsForCross = vtkPoints::New();
        m_gridForCross->SetPoints(m_pointsForCross);

        vtkSmartPointer<vtkIdList> cellHori = vtkSmartPointer<vtkIdList>::New();
        cellHori->InsertNextId(0);
        cellHori->InsertNextId(1);

        vtkSmartPointer<vtkIdList> cellVert = vtkSmartPointer<vtkIdList>::New();
        cellVert->InsertNextId(2);
        cellVert->InsertNextId(3);

        m_gridForCross->InsertNextCell(VTKCellType::VTK_LINE, cellHori);
        m_gridForCross->InsertNextCell(VTKCellType::VTK_LINE, cellVert);

        for (int i = 0; i < 4; i++)
        {
            m_pointsForCross->InsertNextPoint(0., 0., 0.);
        }

        // Create the previewer.
        m_sketchPreviewer = new FITKGraphObjectSketchInteractivePreviewer(m_caseData);
        m_addinGraphObjList.push_back(m_sketchPreviewer);
        
        // Connect the signals.
        connect(m_sketchPreviewer, &FITKGraphObjectSketchInteractivePreviewer::sig_modelingFinished, this, &FITKGraphObjectSketchGrid::sig_modelingFinished);
        connect(m_sketchPreviewer, &FITKGraphObjectSketchInteractivePreviewer::sig_validPressed, this, &FITKGraphObjectSketchGrid::sig_validPressed);
    }

    void FITKGraphObjectSketchGrid::initGridCells(vtkUnstructuredGrid* grid, int size)
    {
        for (int row = 0; row < size ; row ++)
        {
            for (int col = 0; col < size; col++)
            {
                vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
                cell->InsertNextId(row * (size + 1) + col);
                cell->InsertNextId(row * (size + 1) + col + 1);
                cell->InsertNextId((row + 1) * (size + 1) + col + 1);
                cell->InsertNextId((row + 1) * (size + 1) + col);
                grid->InsertNextCell(VTKCellType::VTK_QUAD, cell);
            }
        }
    }

    void FITKGraphObjectSketchGrid::initGridPoints(vtkPoints* points, int size, double globalSize, vtkVector3d up, vtkVector3d right, double* bfPos)
    {
        double inter = globalSize / size;

        // Right for column and the up for row.
        for (int row = 0; row <= size; row++)
        {
            for (int col = 0; col <= size; col++)
            {
                double pos3[3]{ 0., 0., 0. };
                for (int i = 0; i < 3; i++)
                {
                    pos3[i] = bfPos[i] + inter * row * up[i] + inter * col * right[i];
                }

                points->InsertNextPoint(pos3);
            }
        }
    }

    void FITKGraphObjectSketchGrid::updateVisibility()
    {
        // Update the visibility of the sub grid lines.
        updateCameraPosition();
    }

    void FITKGraphObjectSketchGrid::update(bool forceUpdate)
    {
        Q_UNUSED(forceUpdate);

        // Re-generate the points.
        generateDataSet();

        // Update the visibility.
        updateVisibility(); 
    }

    int FITKGraphObjectSketchGrid::getDataId()
    {
        Interface::FITKGeoCommandList* cmdList = dynamic_cast<Interface::FITKGeoCommandList*>(_dataObj);
        if (!cmdList)
        {
            return -1;
        }

        // Get current sketch data.
        Interface::FITKAbsGeoSketch2D* sketchData = cmdList->getCurrentSketchData();
        if (!sketchData)
        {
            return -1;
        }

        return sketchData->getDataObjectID();
    }

    bool FITKGraphObjectSketchGrid::getDataVisibility()
    {
        Interface::FITKGeoCommandList* cmdList = dynamic_cast<Interface::FITKGeoCommandList*>(_dataObj);
        if (!cmdList)
        {
            return false;
        }

        // Get current sketch data.
        Interface::FITKAbsGeoSketch2D* sketchData = cmdList->getCurrentSketchData();
        if (!sketchData)
        {
            return false;
        }

        if (!sketchData->isEnable())
        {
            return false;
        }

        return true;
    }

    void FITKGraphObjectSketchGrid::setVisible(bool visibility)
    {
        if (m_fActorGrid)
        {
            m_fActorGrid->SetVisibility(visibility);
        }

        if (m_fActorSubGrid)
        {
            m_fActorSubGrid->SetVisibility(false);
        }

        if (m_fActorAxis)
        {
            m_fActorAxis->SetVisibility(visibility);
        }

        if (m_fActorCross)
        {
            m_fActorCross->SetVisibility(visibility);
        }

        if (m_textActorPos)
        {
            m_textActorPos->SetVisibility(visibility);
        }

        // Update the visibility of the sketch previewer.
        if (m_sketchPreviewer)
        {
            if (visibility)
            {
                m_sketchPreviewer->clearData();
            }

            m_sketchPreviewer->setVisible(visibility);
        }

        // Update the visibility of the sub grid lines.
        // updateCameraPosition();
    }

    bool FITKGraphObjectSketchGrid::getFixedBounds(double* bounds)
    {
        bool hasSketchBds = false;
        if (m_currentSketchGraphObj)
        {
            hasSketchBds = m_currentSketchGraphObj->getFixedBounds(bounds);
        }

        // If the sketch has valid bounds, return the sketch bounds.
        if (hasSketchBds)
        {
            return true;
        }

        // Use the main grid lines actor bounds.
        if (!m_fActorGrid || !m_fActorGrid->GetVisibility())
        {
            return false;
        }

        double* center = m_fActorGrid->GetCenter();
        m_fActorGrid->GetBounds(bounds);

        // Scale the bounds. ( * 0.2 )
        for (int i = 0; i < 3; i++)
        {
            double scale = qAbs((bounds[i * 2] - center[i]) * 0.8);
            bounds[i * 2] += scale;
            bounds[i * 2 + 1] -= scale;
        }

        return true;
    }

    void FITKGraphObjectSketchGrid::updateCameraNormal()
    {
        // Update the size of the cross.
        updateCross();

        // Update the pipeline.
        if (m_gridForCross)
        {
            m_gridForCross->Modified();
        }
    }

    void FITKGraphObjectSketchGrid::updateCameraPosition()
    {
        // Update the size of the cross.
        updateCross();

        // Update the pipeline.
        if (m_gridForCross)
        {
            m_gridForCross->Modified();
        }

        // Check whether the sub grid lines need to be shown.
        //@{
        if (m_fActorSubGrid)
        {
            if (!m_fRender)
            {
                return;
            }

            vtkRenderer* renderer = m_fRender->getRenderer();
            if (!renderer)
            {
                return;
            }

            vtkCamera* camera = renderer->GetActiveCamera();
            if (!camera)
            {
                return;
            }
     
            if (camera->GetParallelProjection())
            {
                // Get the parallel scale.
                double pScale = camera->GetParallelScale();
                m_fActorSubGrid->SetVisibility((pScale * 5) <= (m_gridWorldSize * m_ratioForSubGrid)
                    && m_fActorGrid->GetVisibility());
            }
            else
            {
                // Calculate the distance from the camera to the grid plane center.
                double* camPos = camera->GetPosition();
                double dist = sqrt(pow(camPos[0] - m_crossPos[0], 2) + pow(camPos[1] - m_crossPos[1], 2) +
                    pow(camPos[2] - m_crossPos[2], 2));
                m_fActorSubGrid->SetVisibility(dist <= m_gridWorldSize * m_ratioForSubGrid
                    && m_fActorGrid->GetVisibility());
            }
        }
        //@}
    }

    void FITKGraphObjectSketchGrid::updateMouseMove(int* pos)
    {
        bool dataVisi = getDataVisibility();
        if (!dataVisi || !m_fRender || !m_pointsForCross)
        {
            return;
        }

        vtkRenderer* renderer = m_fRender->getRenderer();
        if (!renderer)
        {
            return;
        }

        // Project the mouse world position to the grid plane.
        double worldPos3[3]{ 0., 0., 0. };
        bool flag = FITKGraphCommons::GetMouseWorldPositionOnPlane(renderer, pos, m_planeNor, m_planePos, worldPos3);
        if (!flag)
        {
            return;
        }

        // Try to get the nearest position on the sketch first.
        double pxDistSketch = 0.;
        bool hasPosOnSketch = getNearestPositionOnSketch(worldPos3, m_crossPos, pxDistSketch);
        if (!hasPosOnSketch || pxDistSketch < FITKGraphCommons::s_pixelSnipGridPoint)
        {
            // Try to get the nearest position on the grid.
            double pxDistGrid = 0.;
            getNearestPositionOnGrid(worldPos3, m_crossPos, pxDistGrid);
        }
       
        // Update the cross dataset.
        updateCross();

        // Update the pipeline.
        if (m_gridForCross)
        {
            m_gridForCross->Modified();
        }

        // Try to add a new hovered position to the shape.
        if (m_sketchPreviewer)
        {
            double worldPos3[3]{ 0., 0., 0. };
            getEchoPosition(worldPos3);
            m_sketchPreviewer->addPreviewedPosition(worldPos3);
        }

        // Update the text.
        //@{
        if (m_textActorPos)
        {
            // Transfer the 3D position to 2D.
            double crossVec[3]{ m_crossPos[0] - m_planePos[0], m_crossPos[1] - m_planePos[1], m_crossPos[2] - m_planePos[2] };
            double projRight[3], projUp[3];
            vtkMath::ProjectVector(crossVec, m_planeRight, projRight);
            vtkMath::ProjectVector(crossVec, m_planeUp, projUp);

            double rLen = sqrt(pow(projRight[0], 2) + pow(projRight[1], 2) + pow(projRight[2], 2));
            double upLen = sqrt(pow(projUp[0], 2) + pow(projUp[1], 2) + pow(projUp[2], 2));

            // Get the degrees with the project vector and the plane direction.
            double degRight = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(m_planeRight, crossVec));
            double degUp = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(m_planeUp, crossVec));

            double vR = degRight > 90. ? -1. : 1;
            double vU = degUp > 90. ? -1. : 1;

            m_textActorPos->SetInput(QStringToCharA(QString("x:%1, y:%2")
                .arg(rLen * vR, 0, 'f', 2).arg(upLen * vU, 0, 'f', 2)));
        }
        //@}
    }

    void FITKGraphObjectSketchGrid::updateMousePressed(int* pos)
    {
        Q_UNUSED(pos);

        if (m_sketchPreviewer)
        {
            double worldPos3[3]{ 0., 0., 0. };
            getEchoPosition(worldPos3);
            m_sketchPreviewer->addPressedPosition(worldPos3);
        }
    }

    void FITKGraphObjectSketchGrid::getEchoPosition(double* pos)
    {
        for (int i = 0; i < 3; i++)
        {
            pos[i] = m_crossPos[i];
        }
    }

    void FITKGraphObjectSketchGrid::getNearestPositionOnGrid(double* iPos, double* oPos, double & oPxDist)
    {
        oPxDist = 0.;

        // If the renderer is nullptr, return the input position.
        if (!m_fRender)
        {
            for (int i = 0; i < 3; i++)
            {
                oPos[i] = iPos[i];
            }

            return;
        }

        vtkRenderer* renderer = m_fRender->getRenderer();

        // Get the delta values from the center of the grid to the given position.
        double vec3[3]{ iPos[0] - m_planePos[0], iPos[1] - m_planePos[1], iPos[2] - m_planePos[2] };

        // Project the vector to the right and up.
        double projRight[3], projUp[3];
        vtkMath::ProjectVector(vec3, m_planeRight, projRight);
        vtkMath::ProjectVector(vec3, m_planeUp, projUp);

        double rLen = sqrt(pow(projRight[0], 2) + pow(projRight[1], 2) + pow(projRight[2], 2));
        double upLen = sqrt(pow(projUp[0], 2) + pow(projUp[1], 2) + pow(projUp[2], 2));

        // Get the degrees with the project vector and the plane direction.
        double degRight = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(m_planeRight, projRight));
        double degUp = vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(m_planeUp, projUp));

        double vR = degRight > 90. ? -1. : 1;
        double vU = degUp > 90. ? -1. : 1;

        double mPosInput[3];
        vtkInteractorObserver::ComputeWorldToDisplay(renderer, iPos[0], iPos[1], iPos[2], mPosInput);

        // Try to snip to the main grid first.
        //@{
        double mainInterval = m_gridWorldSize / m_numGrid;

        // Get the cell of the position.
        int mainCell2[2]{ int(rLen / mainInterval), int(upLen / mainInterval) };

        // Find out the nearest pos of the cell points.
        double posMain_1[3], posMain_2[3], posMain_3[3], posMain_4[3];
        for (int i = 0; i < 3; i++)
        {
            posMain_1[i] = m_planePos[i] + mainCell2[0] * mainInterval * m_planeRight[i] * vR + mainCell2[1] * mainInterval * m_planeUp[i] * vU;
            posMain_2[i] = m_planePos[i] + (mainCell2[0] + 1) * mainInterval * m_planeRight[i] * vR + mainCell2[1] * mainInterval * m_planeUp[i] * vU;
            posMain_3[i] = m_planePos[i] + mainCell2[0] * mainInterval * m_planeRight[i] * vR + (mainCell2[1]  + 1) * mainInterval * m_planeUp[i] * vU;
            posMain_4[i] = m_planePos[i] + (mainCell2[0] + 1) * mainInterval * m_planeRight[i] * vR + (mainCell2[1] + 1) * mainInterval * m_planeUp[i] * vU;
        }

        double mainNearest3[3]{ posMain_1[0], posMain_1[1], posMain_1[2] };
        getTheNearestPos(iPos, posMain_2, mainNearest3);
        getTheNearestPos(iPos, posMain_3, mainNearest3);
        getTheNearestPos(iPos, posMain_4, mainNearest3);

        // Check the pixel distance.
        double mPosNearestMain[3];
        vtkInteractorObserver::ComputeWorldToDisplay(renderer, mainNearest3[0], mainNearest3[1], mainNearest3[2], mPosNearestMain);
        double pixelDistFromMainToInput = sqrt(pow(mPosInput[0] - mPosNearestMain[0], 2) + pow(mPosInput[1] - mPosNearestMain[1], 2) +
            pow(mPosInput[2] - mPosNearestMain[2], 2));

        if (pixelDistFromMainToInput <= FITKGraphCommons::s_pixelSnipGridPoint)
        {
            for (int i = 0; i < 3; i++)
            {
                oPos[i] = mainNearest3[i];
            }

            oPxDist = pixelDistFromMainToInput;

            return;
        }
        //@}

        // Try to snip to the sub grid lines.
        //@{
        double subInterval = m_gridWorldSize / (m_numGrid * m_numSubGrid);

        // Get the cell of the position.
        int subCell2[2]{ int(rLen / subInterval), int(upLen / subInterval) };

        // Find out the nearest pos of the cell points.
        double posSub_1[3], posSub_2[3], posSub_3[3], posSub_4[3];
        for (int i = 0; i < 3; i++)
        {
            posSub_1[i] = m_planePos[i] + subCell2[0] * subInterval * m_planeRight[i] * vR + subCell2[1] * subInterval * m_planeUp[i] * vU;
            posSub_2[i] = m_planePos[i] + (subCell2[0] + 1) * subInterval * m_planeRight[i] * vR + subCell2[1] * subInterval * m_planeUp[i] * vU;
            posSub_3[i] = m_planePos[i] + subCell2[0] * subInterval * m_planeRight[i] * vR + (subCell2[1] + 1) * subInterval * m_planeUp[i] * vU;
            posSub_4[i] = m_planePos[i] + (subCell2[0] + 1) * subInterval * m_planeRight[i] * vR + (subCell2[1] + 1) * subInterval * m_planeUp[i] * vU;
        }

        double subNearest3[3]{ posSub_1[0], posSub_1[1], posSub_1[2] };
        getTheNearestPos(iPos, posSub_2, subNearest3);
        getTheNearestPos(iPos, posSub_3, subNearest3);
        getTheNearestPos(iPos, posSub_4, subNearest3);

        // Check the pixel distance.
        double mPosNearestSub[3];
        vtkInteractorObserver::ComputeWorldToDisplay(renderer, subNearest3[0], subNearest3[1], subNearest3[2], mPosNearestSub);
        double pixelDistFromSubToInput = sqrt(pow(mPosInput[0] - mPosNearestSub[0], 2) + pow(mPosInput[1] - mPosNearestSub[1], 2) +
            pow(mPosInput[2] - mPosNearestSub[2], 2));

        if (pixelDistFromSubToInput <= FITKGraphCommons::s_pixelSnipGridPoint)
        {
            for (int i = 0; i < 3; i++)
            {
                oPos[i] = subNearest3[i];
            }

            oPxDist = pixelDistFromSubToInput;

            return;
        }
        //@}

        // Failed to snip main or sub grid.
        for (int i = 0; i < 3; i++)
        {
            oPos[i] = iPos[i];
        }
    }

    bool FITKGraphObjectSketchGrid::getNearestPositionOnSketch(double* iPos, double* oPos, double & oPxDist)
    {
        if (!m_currentSketchGraphObj)
        {
            return false;
        }

        // Get the nearest position.
        return m_currentSketchGraphObj->getNearestPosition(iPos, oPos, oPxDist);
    }

    bool FITKGraphObjectSketchGrid::generateDataSet()
    {
        Interface::FITKGeoCommandList* cmdList = dynamic_cast<Interface::FITKGeoCommandList*>(_dataObj);
        if (!cmdList)
        {
            return false;
        }

        // Get current sketch data.
        Interface::FITKAbsGeoSketch2D* sketchData = cmdList->getCurrentSketchData();
        if (!sketchData)
        {
            return false;
        }

        // Get the sketch plane.
        double pos3[3], nor3[3], up3[3];
        sketchData->getPlane(pos3, nor3, up3);

        // Get the right direction of the plane.
        vtkVector3d vecNor = vtkVector3d(nor3).Normalized();
        vtkVector3d vecUp = vtkVector3d(up3).Normalized();
        vtkVector3d vecRight = vecUp.Cross(vecNor).Normalized();

        // Get the default size of the sketch.
        double gridSize = sketchData->getDefaultSize();

        // Get the bottom left position and top right position.
        double topRightPos3[3], btmLeftPos3[3];
        for (int i = 0; i < 3; i++)
        {
            topRightPos3[i] = pos3[i] + (gridSize * 0.5) * vecUp[i] + (gridSize * 0.5) * vecRight[i];
            btmLeftPos3[i] = pos3[i] - (gridSize * 0.5) * vecUp[i] - (gridSize * 0.5) * vecRight[i];
        }

        // Reset the points.
        resetVtkObj(m_pointsForMain);
        resetVtkObj(m_pointsForSub);
        resetVtkObj(m_pointsForAxis);

        // Initialize points.
        initGridPoints(m_pointsForMain, m_numGrid, gridSize, vecUp, vecRight, btmLeftPos3);
        initGridPoints(m_pointsForSub, m_numSubGrid * m_numGrid, gridSize, vecUp, vecRight, btmLeftPos3);

        double topPos[3], btmPos3[3], leftPos3[3], rightPos3[3];
        for (int i = 0; i < 3; i++)
        {
            topPos[i] = pos3[i] + gridSize * 0.5 * vecUp[i];
            btmPos3[i] = pos3[i] - gridSize * 0.5 * vecUp[i];
            leftPos3[i] = pos3[i] - gridSize * 0.5 * vecRight[i];
            rightPos3[i] = pos3[i] + gridSize * 0.5 * vecRight[i];
        }

        m_pointsForAxis->InsertNextPoint(btmPos3);
        m_pointsForAxis->InsertNextPoint(topPos);
        m_pointsForAxis->InsertNextPoint(leftPos3);
        m_pointsForAxis->InsertNextPoint(rightPos3);

        // Save the plane.
        for (int i = 0; i < 3; i++)
        {
            m_planePos[i] = pos3[i];
            m_planeNor[i] = vecNor[i];
            m_planeUp[i] = vecUp[i];
            m_planeRight[i] = vecRight[i];
        }

        // Save the size of the grid.
        m_gridWorldSize = gridSize;

        // Set the plane for previewer.
        if (m_sketchPreviewer)
        {
            m_sketchPreviewer->setPlane(m_planePos, m_planeNor, m_planeUp, m_planeRight, m_gridWorldSize);
        }

        return true;
    }

    void FITKGraphObjectSketchGrid::generateGraph()
    {
        double colorMain3[3]{ 0., 0., 0. };
        double colorSub3[3]{ 0., 0., 0. };
        double colorAxis3[3]{ 0., 0., 0. };

        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_sketchMainLineColor, colorMain3);
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_sketchSubLineColor, colorSub3);
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_sketchAxisColor, colorAxis3);
        
        // Create the actor for main grid lines.
        m_fActorGrid = FITKGraphActor::New();
        m_fActorGrid->setGraphObject(this);
        m_fActorGrid->SetPickable(false);
        m_fActorGrid->SetVisibility(false);
        m_fActorGrid->setScalarVisibility(false);
        m_fActorGrid->GetProperty()->SetRepresentation(1);
        m_fActorGrid->GetProperty()->SetColor(colorMain3);
        m_fActorGrid->GetProperty()->SetLineWidth(1.);
        disableLightShadow(m_fActorGrid);
        addActor(m_fActorGrid);

        // Create the actor for sub grid lines.
        m_fActorSubGrid = FITKGraphActor::New();
        m_fActorSubGrid->setGraphObject(this);
        m_fActorSubGrid->SetPickable(false);
        m_fActorSubGrid->SetVisibility(false);
        m_fActorSubGrid->setScalarVisibility(false);
        m_fActorSubGrid->GetProperty()->SetRepresentation(1);
        m_fActorSubGrid->GetProperty()->SetColor(colorSub3);
        m_fActorSubGrid->GetProperty()->SetLineWidth(1);
        disableLightShadow(m_fActorSubGrid);
        addActor(m_fActorSubGrid);

        // Create the actor for axis lines.
        m_fActorAxis = FITKGraphActor::New();
        m_fActorAxis->setGraphObject(this);
        m_fActorAxis->SetPickable(false);
        m_fActorAxis->SetVisibility(false);
        m_fActorAxis->setScalarVisibility(false);
        m_fActorAxis->GetProperty()->SetRepresentation(1);
        m_fActorAxis->GetProperty()->SetColor(colorAxis3);
        m_fActorAxis->GetProperty()->SetLineWidth(1);
        disableLightShadow(m_fActorAxis);
        addActor(m_fActorAxis);

        // Create the cross actor.
        m_fActorCross = FITKGraphActor::New();
        m_fActorCross->setGraphObject(this);
        m_fActorCross->SetPickable(false);
        m_fActorCross->SetVisibility(true);
        m_fActorCross->setScalarVisibility(false);
        m_fActorCross->GetProperty()->SetRepresentation(1);
        m_fActorCross->GetProperty()->SetColor(1., 1., 1.);
        m_fActorCross->GetProperty()->SetLineWidth(1);
        disableLightShadow(m_fActorCross);
        addActor(m_fActorCross);

        // Initialize the actors' input.
        m_fActorGrid->setInputDataObject(m_gridForMain);
        m_fActorSubGrid->setInputDataObject(m_gridForSub);
        m_fActorAxis->setInputDataObject(m_gridForAxis);
        m_fActorCross->setInputDataObject(m_gridForCross);
        
        // Initialize the polygon offsets.
        m_fActorGrid->setRelativeCoincidentTopologyPolygonOffsetParameters(-0.5);
        m_fActorSubGrid->setRelativeCoincidentTopologyPolygonOffsetParameters(0.);
        m_fActorAxis->setRelativeCoincidentTopologyPolygonOffsetParameters(-1);
        m_fActorCross->setRelativeCoincidentTopologyPolygonOffsetParameters(-1.5);

        // Create the text actor.
        m_textActorPos = vtkTextActor::New();
        m_textActorPos->GetTextProperty()->SetColor(1., 1., 1.);
        m_textActorPos->GetTextProperty()->SetFontSize(16.);
        m_textActorPos->SetVisibility(true);
        m_textActorPos->GetPositionCoordinate()->SetCoordinateSystemToNormalizedDisplay();
        m_textActorPos->SetPosition(0.01, 0.95); // Position

        addActor(m_textActorPos);
    }

    void FITKGraphObjectSketchGrid::disableLightShadow(FITKGraphActor* fActor)
    {
        if (!fActor)
        {
            return;
        }

        fActor->GetProperty()->SetAmbient(1.);
    }

    void FITKGraphObjectSketchGrid::updateCross()
    {
        if (!m_fRender)
        {
            return;
        }

        vtkRenderer* renderer = m_fRender->getRenderer();
        if (!renderer)
        {
            return;
        }

        vtkCamera* camera = renderer->GetActiveCamera();
        if (!camera)
        {
            return;
        }

        // Get the up and right direction of the camera.
        vtkVector3d vecUp = vtkVector3d(camera->GetViewUp()).Normalized();
        vtkVector3d vecNor = vtkVector3d(camera->GetViewPlaneNormal()).Normalized();
        vtkVector3d vecRight = vecNor.Cross(vecUp).Normalized();

        // Re-generate the points of cross dataset.
        resetVtkObj(m_pointsForCross);

        // Get the scale factor for the cross.
        double fator = getCrossScaleFactor();

        // Right for horizon and the up for vertical.
        m_pointsForCross->InsertNextPoint(m_crossPos[0] - vecRight[0] * fator,
            m_crossPos[1] - vecRight[1] * fator, m_crossPos[2] - vecRight[2] * fator);
        m_pointsForCross->InsertNextPoint(m_crossPos[0] + vecRight[0] * fator,
            m_crossPos[1] + vecRight[1] * fator, m_crossPos[2] + vecRight[2] * fator);
        m_pointsForCross->InsertNextPoint(m_crossPos[0] - vecUp[0] * fator,
            m_crossPos[1] - vecUp[1] * fator, m_crossPos[2] - vecUp[2] * fator);
        m_pointsForCross->InsertNextPoint(m_crossPos[0] + vecUp[0] * fator,
            m_crossPos[1] + vecUp[1] * fator, m_crossPos[2] + vecUp[2] * fator);     
    }

    double FITKGraphObjectSketchGrid::getCrossScaleFactor()
    {
        if (!m_fRender)
        {
            return 0.;
        }

        vtkRenderer* renderer = m_fRender->getRenderer();
        if (!renderer)
        {
            return 0.;
        }

        // Get the render window and camera.
        vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
        vtkCamera* camera = renderer->GetActiveCamera();
        if (!renderWindow || !camera)
        {
            return 0.;
        }

        // Get the window size.
        int* ws = renderWindow->GetSize();
        int ww = ws[0];
        if (qFuzzyCompare(ww, 0.))
        {
            return 0.;
        }       

        double scaleFactor = 0.;

        // Calculate the scale factor.      
        if (camera->GetParallelProjection())
        {
            // Get the parallel scale.
            double pScale = camera->GetParallelScale();
            scaleFactor = pScale / double(ww) * 40.;
        }
        else
        {
            // Get the camera position.
            double* cPos = camera->GetPosition();
            double dist = sqrt(vtkMath::Distance2BetweenPoints(cPos, m_crossPos));
            scaleFactor = double(m_crossPixelSize) / double(ww) * dist * 0.6;
        }

        return scaleFactor;
    }

    void FITKGraphObjectSketchGrid::getTheNearestPos(double* tarPos, double* thePos, double* nearestPos)
    {
        double dist = sqrt(pow(tarPos[0] - thePos[0], 2) + pow(tarPos[1] - thePos[1], 2) +
            pow(tarPos[2] - thePos[2], 2));
        double oldDist = sqrt(pow(tarPos[0] - nearestPos[0], 2) + pow(tarPos[1] - nearestPos[1], 2) +
            pow(tarPos[2] - nearestPos[2], 2));
        if (oldDist <= dist)
        {
            return;
        }

        for (int i = 0; i < 3; i++)
        {
            nearestPos[i] = thePos[i];
        }
    }

    void FITKGraphObjectSketchGrid::draw(SketchShapeType type, QVariant param)
    {
        if (!m_sketchPreviewer)
        {
            return;
        }

        m_sketchPreviewer->setShapeType(type, param);
    }

    void FITKGraphObjectSketchGrid::endSketch()
    {
        if (!m_sketchPreviewer)
        {
            return;
        }

        m_sketchPreviewer->endSketch();
    }

    const QVector<double> FITKGraphObjectSketchGrid::getSketchPathPositions()
    {
        if (!m_sketchPreviewer)
        {
            return QVector<double>();
        }

        return m_sketchPreviewer->getPathPositions();
    }

    void FITKGraphObjectSketchGrid::addSketchPosition(double x, double y)
    {
        if (!m_sketchPreviewer)
        {
            return;
        }

        // Transfer the 2D position to 3D.
        double pos3[3]{ 0., 0., 0. };
        for (int i = 0; i < 3; i++)
        {
            pos3[i] = m_planeRight[i] * x + m_planeUp[i] * y;
        }

        double gridPos[3]{ 0., 0., 0. };

        // Try to get the nearest position on the sketch first.
        double pxDistSketch = 0.;
        bool hasPosOnSketch = getNearestPositionOnSketch(pos3, gridPos, pxDistSketch);
        if (!hasPosOnSketch || pxDistSketch < FITKGraphCommons::s_pixelSnipGridPoint)
        {
            // Try to get the nearest position on the grid.
            double pxDistGrid = 0.;
            getNearestPositionOnGrid(pos3, gridPos, pxDistGrid);
        }

        m_sketchPreviewer->addPressedPosition(gridPos);
    }

    void FITKGraphObjectSketchGrid::removeLastSketcPosition()
    {
        if (!m_sketchPreviewer)
        {
            return;
        }

        m_sketchPreviewer->removeLastPressedPosition();
    }

    void FITKGraphObjectSketchGrid::setCurrentSketchGraphObject(FITKGraphObject3D* sketchGraphObj)
    {
        FITKGraphObjectSketch2D* sketchGObj = FITKGraphObjectSketch2D::SafeDownCast(sketchGraphObj);
        if (!sketchGObj)
        {
            return;
        }

        // Try to disconnect the signal.
        if (m_currentSketchGraphObj)
        {
            disconnect(m_currentSketchGraphObj);
        }
        
        // Save the graph object and connect the signal to the new object.
        m_currentSketchGraphObj = sketchGObj;

        if (m_currentSketchGraphObj)
        {
            connect(m_currentSketchGraphObj, &Core::FITKAbstractGraphObject::graphObjDestroyedSig, this, [&]
            {
                m_currentSketchGraphObj = nullptr;
            }, Qt::UniqueConnection);
        }
    }
}   // namespace Exchange